/** @file GOTHanalog.c This file contains source of the API for the ADC and comparator modules
 * of the GOTH Kick-ass Etherboard.
 * Currently, 3 available analog channels of the board are used (AN2, AN4 & AN5 these are RB2,
 * RB4 & RB5 resp.).
 * The RB4 pin can be connected to the on-board temperature sensor.
 *
 * Use AD_AdcMeasure() to measure all the channels and the use AD_AdcGetValue(ADC_CHANNEL_X)
 * to get the decimal value representing the voltage.
 * The voltage can be calculated using this formula:
 *	voltage = (3.3 * AD_AdcGetValue(ADC_CHANNEL_X)) / 1024
 *
 * The comparator has two analog channels (C2INC and C2IND, RG9 and RG8 resp.). They can be
 * compared against a third channel (C2INA, RB3 resp.).
 *
 * @author Geoffrey Ottoy - Tom Hamelinckx
 * @date 05/07/2011 (last modified)
 *
 * @todo add code for reading out temperature or exact voltages in stead of the 10-bit adc value.
 *
 * <pre>
 * MODIFICATION HISTORY:
 *
 * Who  Date     Changes
 * ---- -------- ---------------------------------------------------------
 * GO   01/07/11 Created this file.
 * GO   04/07/11 Implemented and tested AD_InitAdc(), AD_AdcMeasure() and
 *               AD_AdcGetValue().
 * GO	04/07/11 Added and tested AD_InitComparator(), AD_CompareA2C() and
 *               AD_CompareA2D().
 * TH	05/07/11 Corrected some minor issues.
 * </pre>
 * 
 */

#include "GOTHad.h"

/* All the functionality for the comparator */

/** Initialize the comparator module for operation.
 *	2 Channels (C2INC & C2IND) can be compared against another channel (C2INA).
 *	Currently, the comparator works in a polled mode (so no interrupt on output change of 
 *	the comparator).
 *
 * @return nothing
 */
void AD_InitComparator(){
	AD_ComparatorOff();
	
	// set as analog inputs (maybe not necessary)
	AD1PCFGLbits.PCFG3 = 0;		// C2INA
	AD1PCFGLbits.PCFG11 = 0;	// C2INC
	AD1PCFGLbits.PCFG10 = 0;	// C2IND
	
	CM2CONbits.COE = 0;			// no comparator output (only internal)
	CM2CONbits.CPOL = 0;		// non-inverted output
	CM2CONbits.CEVT = 0;		// clear event bit
	CM2CONbits.EVPOL = 0;		// no event generation

	CM2CONbits.CREF = 0;		// C2INA is non-inverting input
}

/** Compare C2INA with C2INC
 *
 * @return 0 if C2INA < C2INC, else 1
 */
int AD_CompareA2C(){
	CM2CONbits.CCH = 1;
	AD_ComparatorOn();
	if(CM2CONbits.COUT == 0){
		AD_ComparatorOff();
		return 0; // C2INA < C2INC
	}
	else{
		AD_ComparatorOff();
		return 1; // C2INA > C2INC
	}
	
}

/** Compare C2INA with C2IND
 *
 * @return 0 if C2INA < C2IND, else 1
 */
int AD_CompareA2D(){
	CM2CONbits.CCH = 2;
	AD_ComparatorOn();
	if(CM2CONbits.COUT == 0){
		AD_ComparatorOff();
		return 0; // C2INA < C2IND
	}
	else{
		AD_ComparatorOff();
		return 1; // C2INA > C2IND
	}
	
}


/* All the functionality for the ADC */

/*volatile*/int adc_result[ADC_NR_OF_CHANNELS];

/** Initialize the ADC module for operation.
 *		3 Channels are used: AN2, AN4, AN5.
 *
 * @return nothing
 */
void AD_InitAdc(){
	AD_AdcOff();					// make sure the ADC is off before configuring it
	AD1PCFGLbits.PCFG2 = 0;		// analog pin
	AD1PCFGLbits.PCFG4 = 0;		// analog pin
	AD1PCFGLbits.PCFG5 = 0;		// analog pin
	AD1CSSL = 0x0034;			// Include channel 2, 4 and 5 in scan
	
	AD1CON1 = 0x00E0;			// Internal counter triggers conversion
								// Format: xxxx xxdd dddd dddd
	AD1CON3 = 0x0F00;			// Sample time = 15Tad, Tad = Tcy
								// Use system clock
								///@todo check Tad en Tcy settings
	AD1CON2 = 0x0408;			// Set AD1IF after every 3 samples, enable scanning
								// use channels in AD1CSSL as MUX A input
}

/** Perform a measurement (scan the selected channels and store the result).
 *
 * @return nothing
 */
void AD_AdcMeasure(){
	AD_AdcOn();					// turn ADC on

	IFS0bits.AD1IF = 0;			// clear ADC interrupt flag
	AD1CON1bits.ASAM = 1;		// auto start sampling for 31Tad

	while (!IFS0bits.AD1IF);	// conversion done?
	AD1CON1bits.ASAM = 0;		// yes then stop sample/convert

	adc_result[0] = ADC1BUF0;	// AN2
	adc_result[1] = ADC1BUF1;	// AN4 (temp. sensor)
	adc_result[2] = ADC1BUF2;	// AN5

	AD_AdcOff();					// turn ADC off
}

/** Get the measured value (0-1023) for a requested channel.
 * voltage = (3.3 * AD_AdcGetValue(ADC_CHANNEL_X)) / 1024
 *
 * @return the requested value
 */
int AD_AdcGetValue(int channel){
	//ADC_Measure();				// perform a measurement on all the analog channels
	return adc_result[channel]; // return requested value
}

/** The ADC1 isr (currently not used)
 *
 * @return	nothing
 */
void __attribute__((interrupt,no_auto_psv)) _ADC1Interrupt(void){

	IFS0bits.AD1IF = 0;		//Clear interrupt status bit
}
